SERVEI DE CUES (JMS)







Introducció

Propòsit

Aquest servei permet configurar i usar de forma senzilla la infraestructura de missatgeria estándard JMS (Java Messaging Service) de J2EE. Aquest estàndar defineix dues modalitats:

  1. Producció de missatges
  2. Consum de missatges, que pot ser asíncron.

El consum de missatges assíncrons a l'estàndar J2EE es realitza mitjançant "Message-Driven Beans". Canigó no s'incorpora cap mecanisme específic pel consum de missatges asíncrons, i la seva disponibilitat dependrà de la plataforma (especificament del Servidor d'aplicacions).

Així doncs, l'enfoc del servei és el de simplificar la publicació de missatges i la definició i configuració de destinataris.

Context i Escenaris d'Ús

El Servei d'Integració de Cues JMS es troba ubicat dins els serveis continguts a la capa de Dades/Integració de canigo.

Versions i Dependències

Les dependències descrites a la següent url són requerides per tal de compilar i fer funcionar el projecte:
Dependències Servei de Cues

A qui va dirigit

Aquest document va dirigit als següents perfils:

  1. Programador. Per conéixer l'ús del servei
  2. Arquitecte. Per conéixer quins són els components i la configuració del servei
  3. Administrador. Per conéixer com configurar el servei en cadascun dels entorns en cas de necessitat

Documents i Fonts de Referència

[1] Spring JMS http://static.springframework.org/spring/docs/1.2.x/reference/jms.html

Glossari

JMS
JMS o Java Message Service es un estàndard de missatgeria que permet als components de les aplicacions basades en la plataforma J2EE, crear, enviar, rebre i llegir missatges de manera sincrona i asincrona.

ConnectionFactory
Factoria encarregada de crear una connexió al proveidor del sistema de missatges.

Destination
Punts de destí dels missatges que s'envien.

Descripció Detallada

Missatgeria Publicació/Subscripció

La missatgeria de publicació/subscripció permet a una aplicació enviar missatges a una o múltiples aplicacions. Aquestes aplicacions envien i reben missatges subscrivint-se a un servidor de missatges a una cua o tòpic determinat.

Hi ha dos tipus de missatge depenent de com es consumeixen:

  • Queue: en el cas d'un missatge enviat a una cua, encara que hi hagi varis subscriptors a aquests missatges, únicament es processarà una única vegada pel primer subscriptor que estigui lliure.
  • Topic: en el cas d'un missatge enviat a un tòpic, tots els subscriptors a aquests missatges els processaran.

Els consumidors d'un missatge poden ser síncrons o asíncrons. En general, els consumidors asíncrons escalen millor i tenen un rendiment superior.

Per aplicacions que s'executen al costat servidor, gairebé sempre sempre s'utilitzaran consumidors asíncrons. Concretament per aplicacions J2ee es fa mitjançant Message-Driven Beans (MDB's) que són EJB's configurats per escoltar i subscriures a cues o tòpics del servidor d'aplicacions.

A continuació es destaquen alguns paràmetres de configuració dels missatges JMS que cal tenir en compte en el desenvolupament d'aplicacions que publiquin o consumeixin aquests tipus de missatges.

Persistència del missatge

Els missatges JMS poden ser especificats com a persistents o no-persistents.

  • Un missatge persistent garanteix la seva entrega un i només un cop. El missatge no pot ser perdut degut a una fallida del proveïdor JMS, i no pot ser entregat dos cops. No es considerat enviat el missatge fins que no ha estat persistit (a un fitxer ó a una base de dades).
  • Un missatge no-persistent no és emmagatzemat. Està garantit que sigui entregat com a molt un cop, però si hi ha una fallida del proveïdor JMS el missatge pot ser perdut.

Els missatges persistents tenen pitjor rendiment degut al pas addicional d'haver-se d'escriure en disc o a base de dades. Només es recomana utilitzar missatges persistents pels casos d'ús crítics, on no es pot permetre la pèrdua del missatge (p.ex. una trasacció bancària, ...).

Destinacions distribuïdes

Una destinació distribuïda és un conjunt de destinacions que són accessibles com a una única destinació lògica al client. Disposen d'una sola adreça JNDI, i els membres d'aquest conjunt normalment són distribuïts pels membres d'un cluster, on cada destinació pertany a un servidor JMS separat.

Les aplicacions que usen destinacions distribuïdes tenen més disponibilitat respecte a les tradicionals, ja que els servidor (Weblogic) proporciona balanceig de càrrega i tolerància a fallades pels membres d'una destinació distribuïda en un cluster.

Per tant, es recomana l'ús de destinacions distribuïdes sempre que les aplicacions publicadores/consumidores es desplegin en un cluster de Weblogic.

Arquitectura i Components

Els components podem classificar-los en:

  1. Interfícies i Components Genérics. Interfícies del servei i components d'ús general amb independència de la implementació escollida.
  2. Implementació basada en Spring
    Es pot trobar tota la documentació JavaDoc i el codi font referent aquests components a les següents urls:

JavaDoc:  http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-jms/xref/index.html
Codi Font:  http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-jms/apidocs/index.html

Instal.lació i Configuració

Instal.lació

La instal.lació del servei requereix de la utilització de la llibreria 'canigo-services-jms' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'.

Configuració

La configuració del servei implica els següents pasos:

  1. Definir la implementació del Servei que s'usarà
  2. Definir la localització del fitxer de propietats del Servei
  3. Definir les propietats del Servei

Definició del Servei

Fitxer de configuració: canigo-services-jms.xml

Ubicació proposada: <PROJECT_ROOT>/src/main/resources/spring

En aquest apartat configurarem el bean de Servei de Cues, és a dir, la implementació que es farà servir en l'atribut 'class'. En l'actualitat s'ofereix la classe ' net.gencat.ctti.canigo.services.jms.impl.JmsServiceImpl'

<bean  id="JmsService"

class="net.gencat.ctti.canigo.services.jms.impl.JmsServiceImpl">

...

</bean>



Podem definir les següents propietats:

Propietat Requerit Descripció
logService No Referència al Servei de Traces
namingProviderUrl La URL del servei JNDI (protocol TCP o RMI, generalment), que permetrà localitzar el servei JMS remot
namingContextFactory La implementació de la factoriia JNDI que permet localitzar el servei. Exemple: Per OpenJMS s'ha d'especificar "org.exolab.jms.jndi.InitialContextFactory"
connectionFactoryName Nom JNDI amb el que es troba registrada la factoria de connexions JMS
defaultDestinationName Nom del destí (cua o tópic) en el servidor JMS remot
namingPrincipal Usuari
namingCredentials Password

Per les propietats es recomana que siguin definides de forma externa a un fitxer de propietats i referenciades aquí amb el format ${nom} (veure 'Definir les propietats del Servei').

En cas de que volguessim fer ús de diferents destins de publicació es farà ús de la propietat 'configurationMap', on la clau correspondrà al nom de la configuració (accessible després amb el mètode 'getJmsOperations(nom)') i podrem especificar les propietats indicades a dalt pel valor. Exemple:

<bean  id="JmsService"

class="net.gencat.ctti.canigo.services.jms.impl.JmsServiceImpl">

...

<property  name="configurationMap">

<map>

<entry  key="otherConfig">

<bean  parent="baseJmsConfigurator">

<property name="defaultDestinationName"  value="topic2"/>

<property name="namingContextFactory"  value="org.exolab.jms.jndi.InitialContextFactory"/>

<property  name="namingProviderUrl" value="rmi://localhost:1099"/>

<property  name="namingPrincipal" value="admin"/>

<property  name="namingCredentials" value="openjms"/>

<property  name="connectionFactoryName"  value="ConnectionFactory"/>

</bean>

</entry>

</map>

</bean>



Definició de la Localització del Fitxer de Propietats del Servei


Fitxer de configuració: canigo-services-configuration.xmlUbicació proposada: <PROJECT_ROOT>/src/main/resources/spring

Seguint el propósit general del Servei de Configuració definirem a la propietat 'basePropertyFiles' una nova localització pel fitxer de propietats del servei.

Exemple:

<bean id="configurationService"  class="net.gencat.ctti.canigo.services.configuration.
springframework.beans.factory.config.HostPropertyPlaceholderConfigurer">
	<property  name="basePropertyFiles">
		<list>
		...
			<value>classpath:jms/jms.properties</value>
		</list>
	</property>
</bean>


A l'exemple, s'ha definit la ubicació del fitxer a 'classpath:jms/jms.properties' (veure següent pas).

Definició de les Propietats del Servei
Fitxer de configuració: jms.properties

Ubicació proposada: <PROJECT_ROOT>/src/main/resources/jms

En aquest fitxer definirem les propietats que permet el servei (veure 'Definició del Servei')
Exemple:

jmsService.namingProviderUrl=rmi://localhost:1099

jmsService.namingContextFactory=org.exolab.jms.jndi.InitialContextFactory

jmsService.connectionFactoryName=ConnectionFactory

jmsService.defaultDestinationName=topic1

jmsService.namingPrincipal=admin

jmsService.namingCredentials=openjms



En aquest cas, la referència en la definició del servei seria (només es mostra la referència a la primera propietat definida):
<property name="namingProviderUrl"  value="$\{jmsService.namingProviderUrl\}"/>

...



Utilització del Servei

S'han de seguir els següents pasos:

  • Obtenir el destí JMS

El mètode "getDefaultJmsOperations" permet obtenir el destí JMS per defecte que es configura junt amb el servei, mentre que "getJmsOperations" permet obtenir una configuració diferent, identificada per un nom.

  • Una vegada tenim la interfície 'JmsServiceOperations' podem realitzar les operacions que s'especifiquen a la seva interfície.
    public interface JmsServiceOperations {
    
        void convertAndSend(Object message) throws JmsServiceException;
        void convertAndSend(Destination destination, Object message)
                throws JmsServiceException;
        void convertAndSend(String destinationName, Object message)
                throws JmsServiceException;
        Message receive() throws JmsServiceException;
        Message receive(Destination destination) throws JmsServiceException;
        Message receive(String destinationName) throws    JmsServiceException;
        Message receiveSelected(String messageSelector) throws JmsServiceException;
        Message receiveSelected(Destination destination, String messageSelector)
                throws JmsServiceException;
        Message receiveSelected(String destinationName, String messageSelector)
                throws JmsServiceException;
        Object receiveAndConvert() throws JmsServiceException;
        Object receiveAndConvert(Destination destination)
                throws JmsServiceException;
        Object receiveAndConvert(String destinationName) throws JmsServiceException;
        Object receiveSelectedAndConvert(String messageSelector)
                throws JmsServiceException;
        Object receiveSelectedAndConvert(Destination destination,
                String messageSelector) throws JmsServiceException;
        Object receiveSelectedAndConvert(String destinationName,
                String messageSelector) throws JmsServiceException;
    
    }


    Per conéixer com usar de cadascuna de les operacions es requereix un coneixement previ de JMS.

Eines de Suport

OpenJMS

En cas de no disposar d'un Servidor de Cues per a realitzar les proves, es pot fer ús de OpenJMS. Accedir a http://openjms.sourceforge.net/ per a més referència.

Integració amb Altres Serveis

Integració amb el Servei Multiidioma i Excepcions

El servei llença vàries excepcions amb codis per tal que el missatge pugui ser internacionalitzat. En el següent exemple es mostren els codis que s'han de definir:

canigo.services.jms.jndi_lookup_failed=Jndi lookup failed for \{0\}



Exemples

Exemple d'Enviament a una Destinació

Exemple de Prova Unitària

Obtenim el context Spring de test, això no cal fer-ho explícitament en una aplicació web

BeanFactory beanFactory = new  ClassPathXmlApplicationContext("applicationContext.xml");


//Obtenim el servei pròpiament dit
JmsServiceImpl JmsService = (JmsServiceImpl)beanFactory.getBean("JmsService");
JmsServiceOperations operations = (JmsServiceOperations)


Ara es pot fer servir l'interface JmsServiceOperations ...
operations.convertAndSend("A String message to the default destination")



Exemple des d'una classe Action


JmsServiceUtils.getOperations(request).convertAndSend("testMessage");



En aquest cas s'ha fet ús del mètode estàtic "getOperations" de la classe auxiliar
"net.gencat.ctti.canigo.services.jms.JmsServiceUtils"
Aquest mètode permet utilitzar el servei sense haver de localitzar-lo expressament.